#include <iostream>
#include "RtspClient.h"

#include "SessionDescription.h"

namespace rtsp {

RtspClient::RtspClient(const std::string& rtspUrl, asio::io_context& ioContext)
  : ioContext_(ioContext)
  , resolver_(ioContext)
  , socket_(ioContext)
  , absUrl_(rtspUrl)
{
    auto pos = rtspUrl.find_first_of("rtsp://");
    if(pos == std::string::npos){
        return;
    }

    std::string rtspUrl2 = rtspUrl.substr(pos + 7);

    auto pos3 = rtspUrl2.find_last_of("@");
    if(pos3 == std::string::npos){
        pos3 = rtspUrl2.find(":");
        auto pos4 = rtspUrl2.find("/");

        if(pos3 == std::string::npos){
            address_ = rtspUrl2.substr(0, pos4);
            port_ = "554";
        }
        else{
            address_ = rtspUrl2.substr(0, pos3);
            port_ = rtspUrl2.substr(pos3 + 1, pos4 - pos3 - 1);
        }
         absPath_ = rtspUrl2.substr(pos4 + 1);
       
    }
    else{
        auto userInfo = rtspUrl2.substr(0, pos3);
        auto addressInfo =  rtspUrl2.substr(pos3 + 1);

        pos3 = userInfo.find_last_of(":");
        userName_ = userInfo.substr(0, pos3);
        passWord_ = userInfo.substr(pos3 + 1);

        pos3 = addressInfo.find(":");
        auto pos4 = addressInfo.find("/");
        if(pos3 == std::string::npos){
            address_ = addressInfo.substr(0, pos4);
            port_ = "554";
        }
        else{
            address_ = addressInfo.substr(0, pos3);
            port_ = addressInfo.substr(pos3 + 1, pos4 - pos3 - 1);
        }

        absPath_ = addressInfo.substr(pos4 + 1);
    }

    std::cout << "address: "<< address_ << std::endl;
    std::cout << "port: "<< port_ << std::endl;
    std::cout << "username: "<< userName_ << std::endl;
    std::cout << "password: "<< passWord_ << std::endl;
    std::cout << "absPath: "<< absPath_ << std::endl;
}

RtspClient::~RtspClient(){
  std::cout << absUrl_ << " closed " << std::endl;
}

//RtspClient::RtspClient(const std::string& address, const std::string& port, const std::string& absPath)
//{
//}

void RtspClient::start()
{
     auto endpoints = resolver_.resolve(address_, port_);
     auto self = shared_from_this();
     asio::async_connect(socket_, endpoints,[&, self](std::error_code ec, tcp::endpoint){
          if (!ec){
            onOption();
          }
        });
}

 void RtspClient::onOption(){
    std::ostream request_stream(&request_);
    request_stream << "OPTIONS " << absUrl_ << " RTSP/1.0\r\n";
    request_stream << "CSeq:" << seq_++ << "\r\n\r\n";
    auto self = shared_from_this();
    asio::async_write(socket_, request_, [&, self](std::error_code ec, std::size_t /*length*/){
          if (!ec){
            readStatusLine();
          }
          else{
            std::cout <<"send options failed " << ec.value() <<" " << ec.message() << std::endl;
          }
        });

    method_ = Method::kOption;
 }

void RtspClient::readStatusLine(){
    auto self = shared_from_this();
    asio::async_read_until(socket_, response_, "\r\n", [&, self](std::error_code ec, std::size_t /*length*/){
          if(!ec){
            onStatusLine();
          }
          else{
            std::cout <<"get options result failed " << ec.value() <<" " << ec.message() << std::endl;
          }
    });
}

 void RtspClient::onStatusLine(){
      std::istream responseStream(&response_);
      std::string rtspVersion;
      responseStream >> rtspVersion;
      unsigned int statusCode;
      responseStream >> statusCode;
      std::string statusMessage;
      std::getline(responseStream, statusMessage);
      if (!responseStream || rtspVersion.substr(0, 5) != "RTSP/")
      {
        std::cout << "Invalid response " << std::endl;
        return;
      }
      if (statusCode != 200)
      {
        std::cout << "Response returned with status code " << statusCode << std::endl;
        return;
      }

      // Read the response headers, which are terminated by a blank line.
      auto self = shared_from_this();
      asio::async_read_until(socket_, response_, "\r\n\r\n",[&, self](std::error_code ec, std::size_t /*length*/){
         if(!ec){
            readHeaders();
          }
          else{
            std::cout <<"read status line failed " << ec.value() <<" " << ec.message() << std::endl;
          }
      });
 }

void RtspClient::readHeaders(){
    std::istream response_stream(&response_);
    std::string header;
    while (std::getline(response_stream, header) && header != "\r"){
        std::cout << header << std::endl;
    }
      
    std::cout << "======head end====="<< std::endl;

    switch(method_){
      case Method::kOption: onDescribe(); break;
      case Method::kDescribe: readSdp(); break;
      default: break;
    } 
}
    
void RtspClient::onDescribe(){
    std::ostream request_stream(&request_);
    request_stream << "DESCRIBE " << absUrl_ << " RTSP/1.0\r\n";
    request_stream << "CSeq:" << seq_++ << "\r\n";
    request_stream << "Accept:" << " application/sdp" << "\r\n\r\n";

    auto self = shared_from_this();
    asio::async_write(socket_, request_, [&, self](std::error_code ec, std::size_t /*length*/){
          if (!ec){
            readStatusLine();
          }
          else{
            std::cout <<"send describe failed " << ec.value() <<" " << ec.message() << std::endl;
          }
        });

    method_ = Method::kDescribe;
}

void RtspClient::readSdp(){
   auto self = shared_from_this();
   asio::async_read(socket_, response_, asio::transfer_at_least(1),[&, self](std::error_code ec, std::size_t /*length*/){
          if (!ec){
            onSdp();
          }
          else{
            std::cout <<"get sdp failed " << ec.value() <<" " << ec.message() << std::endl;
          }
   });
}

void RtspClient::onSdp(){
    auto cbt = response_.data();
    std::string sdpInfo(asio::buffers_begin(cbt), asio::buffers_end(cbt));
    std::cout << "sdp: " << std::endl;
    std::cout << sdpInfo << std::endl;

    try {
		  auto sdp = sdp::SessionDescription::parse(sdpInfo);
      auto origin = sdp->getOrigin();
      std::cout <<"type: " << origin->getAddrtype() << std::endl;
      std::cout << "ip: " << origin->getAddress() << std::endl;

      auto video = sdp->getMedia("video");
      auto rtpmap = video->getAttribute<sdp::RTPMapAttribute>();
      std::cout << "format: " << rtpmap->getFormat() << std::endl;
      std::cout << "rate: " << rtpmap->getRate() << std::endl;
      std::cout << "encode: " << rtpmap->getName() << std::endl;
	  } catch (const std::exception& e) { 
		  std::cout << e.what();
	  }
}

}

